"""
Database Handler stores the information pertaining
to the current state of the mail client
Written By: Daniel Ortiz
"""
import processing
import datetime, time, mimetypes
from email.Utils import parsedate
from Queue import Empty
from database.broker import ABroker
from database.dbinterface import DatabaseProcess
from database.mapclass import *
from gui.startup import SetupWizard
from gui.frames.login import UserPassFrame
from mailbox import MailboxHandler
from network.imap4 import IMAP4Factory
from network.pop3 import POP3Factory
from twisted.internet import reactor, defer, protocol, ssl
from twisted.mail import imap4
from email.charset import Charset
import email, random, pprint

# FROM Python Cookbook, Martelli
def select(data, n):
    " Find the nth rank ordered element (the least value has rank 0). "
    # make a new list, deal with <0 indices, check for valid index
    data = list(data)
    if n<0:
        n += len(data)
    if not 0 <= n < len(data):
        raise ValueError, "can't get rank %d out of %d" % (n, len(data))
    # main loop, quicksort-like but with no need for recursion
    while True:
        pivot = random.choice(data)
        pcount = 0
        under, over = [  ], [  ]
        uappend, oappend = under.append, over.append
        for elem in data:
            if elem < pivot:
                uappend(elem)
            elif elem > pivot:
                oappend(elem)
            else:
                pcount += 1
        numunder = len(under)
        if n < numunder:
            data = under
        elif n < numunder + pcount:
            return pivot
        else:
            data = over
            n -= numunder + pcount\

class DatabaseHandler(object):
    def __init__(self, parent, broker):
        self.broker = broker
        self.parent = parent
        self.Variables()
        self.HandleStartup()
        #self.broker.startup().addCallback(self.HandleStartup).addErrback(self.PrintError)
        
    def PrintError(self, error):
        print 'Error:', error 
        
    def Variables(self):
        self.connections = {}
        self.mailbox_mail = {}
        self.mailbox_folder = {}
        self.mailbox_display = {}
        self.meta = {'sent': 'META:Sent', 'trash': 'META:Trash', 'draft' : 'META:Draft' ,'inbox' : 'META:Inbox'}
        self.charset = Charset()

        
    def HandleStartup(self):
        self.broker.GetData(session = True).addCallback(self.HandleData).addErrback(self.PrintError)
        
    def HandleData(self, *data):
        self.mailbox_mail = data[0][0]
        self.current_mailbox = self.mailbox_mail.keys()[0]
        self.book_address = data[0][1]
        self.SynchServer() 
        self.VisualizeMail()
        
    def BindMailList(self, mail_list):
        self.mail_list = mail_list
        
    def BindFolderTree(self, folder_tree):
        self.folder_tree = folder_tree
        
    def GetFolderTree(self):
        return self.folder_tree
        
    def BindMailNotebook(self, notebook):
        self.notebook = notebook
        
    def VisualizeMail(self):
        for mailbox, mail in self.mailbox_mail.iteritems():
            self.DisplayMail(mail, mailbox)
            
    def DisplayMail(self, mail, mailbox):
        folder = []
        for m in mail:
            self.__AddToFolder(folder, m.folder, m)
        self.DisplayMailInGUI(mailbox, folder)
    
    def GetMeta(self, meta_folder):
        """Return all mail that falls into a characteristic meta folder"""
        ret = []
        for mailbox, mail in self.mailbox_mail.iteritems():
            for m in mail:
                if m.flags and '\\Deleted' in m.flags:
                    if meta_folder == 'trash':
                        ret.append(m)
                    continue
                elif m.local_flags and '\\Deleted' in m.local_flags:
                    if meta_folder == 'trash':
                        ret.append(m)
                    continue
                if m.folder.lower().find(meta_folder) != -1:
                    ret.append(m)
        return ret
    
    def GetMailboxMail(self, mail):
        for mailbox, mail_list in self.mailbox_mail.iteritems():
            for m in mail_list:
                if m == mail:
                    return mailbox
                    
    def GetMetaFolder(self, m):
        """Get the meta folder corresponding to an email"""
        if m.flags and '\\Deleted' in m.flags:
            return self.meta['trash']
        elif m.local_flags and '\\Deleted' in m.local_flags:
            return self.meta['trash']
        l = m.folder.lower()
        for d in self.meta.keys():
            if l.find(d) != -1:
                return self.meta[d]
        return None
    def __AddToFolder(self, folder, key, mail):
        if not key in folder:
            folder.append(key)
        
    def DisplayMailInGUI(self, mailbox, folder):
        if not self.mailbox_display.has_key(mailbox):
            self.folder_tree.AddMailbox(mailbox)
            self.mailbox_display[mailbox] = True
        self.folder_tree.AddFolder(mailbox, folder)
        if not self.mailbox_folder.has_key(mailbox):
            self.mailbox_folder[mailbox] = []
        if isinstance(folder, basestring):
            self.mailbox_folder[mailbox].append(folder)
        elif isinstance(folder, list):
            for f in folder:
                self.mailbox_folder[mailbox].append(f)

    def HasMailbox(self):
        """Checks to see if a mailbox is setup"""
        return len(self.mailbox_mail.keys()) > 0
    
    def GetMailboxFolderMail(self, mailbox, folder):
        """Return the mail in a certain folder of a mailbox"""
        print folder, self.mailbox_folder[mailbox]
        return [m for m in self.mailbox_mail[mailbox] if m.folder == folder]
    
    def SynchServer(self):
        if not self.connections:
            for mailbox, mail in self.mailbox_mail.iteritems():
                self.StartConnection(mailbox, sync = True)
        else:
            first = True
            time = datetime.datetime.now()
            for mailbox, factory in self.connections.iteritems():
                mailbox.startupTime(time)
                folder = self.mailbox_folder[mailbox]
                if isinstance(mailbox, IMAP4Factory):
                    self.SetSynchGauge(folder[:], clear = first)
                elif isinstance(mailbox, POP3Factory):
                    self.SetSynchGauge(len(factory.proto.mUIDS) - len(self.mailbox_mail[mailbox]), clear = first)
                factory.sync = True
                factory.proto.Synchronize(folder[:])
                first = False
    
    def StartConnection(self, mailbox, sync = False):
        if mailbox.protocol == 'pop3':
            f = POP3Factory(str(mailbox.username), str(mailbox.password), self.HandlePOP3Results, mailbox, h = self)
        elif mailbox.protocol == 'imap4':
            f = IMAP4Factory(str(mailbox.username), str(mailbox.password), self.CHMailboxList,
                             self.HandleIMAP4Results, mailbox, h = self, s = sync)
        if mailbox.issl:
            reactor.connectSSL(str(mailbox.iserver), mailbox.iport, f, ssl.ClientContextFactory())
        else:
            reactor.connectTCP(str(mailbox.iserver), mailbox.iport, f)      
        self.connections[mailbox] = f
        mailbox.startupTime(datetime.datetime.now())
        print datetime.datetime.now()
        
    def HandlePOP3Results(self, *args, **kwargs):
        def Copy():
            kwargs['old_m'] = kwargs['new_m']
        d = self.broker.UpdateMailbox(kwargs['old_m'], kwargs['new_m'], session= True)
        d.addCallback(lambda _: Copy())
        
    def OnIdle(self):
        time = datetime.datetime.now()
        first = True
        for mailbox, factory in self.connections.iteritems():
            if mailbox.isTime(time):
                mailbox.startupTime(time)
                folder = self.mailbox_folder[mailbox]
                if isinstance(mailbox, IMAP4Factory):
                    self.SetSynchGauge(folder[:], clear = first)
                elif isinstance(mailbox, POP3Factory):
                    self.SetSynchGauge(len(factory.proto.mUIDS) - len(self.mailbox_mail[mailbox]), clear = first)
                factory.sync = True
                factory.proto.Synchronize(folder[:])
                first = False
                
    def CHMailboxList(self, mlist, client):
        """Cycles through the folders of the IMAP4 database, querying for the
        relevant data."""
        def HandleError(results):
            """Print out the error for debugging"""
            return
        if mlist:
            m = mlist.pop(0)
            client.GetMailbox(m, mlist).addErrback(HandleError)
            
    def HandleIMAP4Results(self,mail_list, client):
        """Handle the IMAP4 results, and save the relevant mail data"""
        self.CHMailboxList(mail_list, client)
                
    def OnQuit(self):
        def IgnoreError(error):
            pass
        def CloseNetwork(error):
            """Ignore any error since we are quitting"""
            print error
            d = self.CloseNetworkConnections()
            return d.addBoth(IgnoreError)
        print 'Inside'
        d = self.broker.Close(session = True)
        d = d.addBoth(CloseNetwork)
        return d
    
    
    def CloseNetworkConnections(self):
        """Close the network connections return a deferred"""
        def IgnoreError(error):
            pass
        for mailbox, f in self.connections.iteritems():
            if isinstance(f, POP3Factory):
                d = f.proto.quit().addErrback(IgnoreError)
            elif isinstance(f, IMAP4Factory):
                d = f.proto.logout().addErrback(IgnoreError)
        return defer.succeed(True)
                
    def ShowMail(self, mail, mailbox):
        f = self.connections[mailbox]
        self.SetCurrentMailbox(mailbox)
        if isinstance(f, POP3Factory):
            self.HandleBodyRetrieval(mail.body, mail = mail, mailbox = mailbox, local = True)
        elif isinstance(f, IMAP4Factory):
            m = imap4.MessageSet()
            m.add(int(mail.uid))
            d = f.proto.select(mail.folder)
            d.addCallback(lambda _: f.proto.fetchMessage(m, uid = True))
            d.addCallback(self.HandleBodyRetrieval, mail = mail, mailbox = mailbox, local = False, mailbox = mailbox)
            d.addErrback(self.PrintError)
            
    def HandleBodyRetrieval(self, message, **kwargs):
        mail = kwargs['mail']
        mailbox = kwargs['mailbox']
        attachments = []
        if isinstance(message, dict):
            if not message.values():
                self.ShowMail(kwargs['mail'], kwargs['mailbox'])
                return defer.succeed(True)
        if not kwargs['local']:
            data = email.message_from_string(message.values()[0]['RFC822'])
        else:
            data = email.message_from_string(message)
        print data.keys(), data['Content-Type'], data['Content-Transfer-Encoding']
        mail.encoding = data['Content-Transfer-Encoding']
        mail.charset = data.get_content_charset()
        payload = data.get_payload()
        if isinstance(payload, str):
            mail.body = payload
            mail.content_type = data.get_content_type()
        else:
            alternative = False
            mixed = False
            entry = False
            # Iterate over the entire message structure. Parse accordingly
            for part in data.walk():
                if part.is_multipart():
                    if part.get_content_subtype() == 'alternative':
                        alternative = True
                        mixed = False
                    elif part.get_content_subtype() == 'mixed':
                        mixed = True
                        alternative = False
                    elif part.get_content_subtype() == 'related':
                        pass
                    else:
                        print 'Error:', part.get_content_type()
                    continue
                if part.get_content_maintype() == 'text':
                    load = part.get_payload()
                    filename = part.get_filename()
                    #FCFS: to deal with erroneous and/or complicated MIME architectures
                    if not entry:
                        entry = True
                        mail.body = load
                        mail.content_type = part.get_content_type()
                    mail.alternative = alternative
                    print 'Encoding: %s' % part['Content-Transfer-Encoding'], part.get_content_type()
                    att = Attachment(filename, load[:], len(load), part['Content-Transfer-Encoding'], 
                                     content_type = part.get_content_type(), charset = part.get_content_charset(),
                                     alternative = alternative)
                    attachments.append(att)
                    continue
                elif part.get_content_maintype() == 'application':
                    load = part.get_payload()
                    filename = part.get_filename()
                    att = Attachment(filename, load, len(load), part.get_content_type())
                    attachments.append(att)
                    continue
                name = part.get_filename(None)
                if not name:
                    ext = mimetypes.guess_extension(part.get_content_type())
                    name = 'anon%s' % ext 
                print 'Error:', part.get_content_type()
        self.notebook.NewMail(mail, attachments, mailbox)
        
    def MarkAsDeleted(self, mail, mailbox):
        """Mark the mail as locally deleted"""
        old_mail = Mail(*mail.get())
        mail.local_flags = '\\Deleted'
        self.broker.UpdateMail(old_mail, mail, mailbox, session = True)
        self.notebook.DeletePanel(mail)
        
    def UnMarkAsDeleted(self, mail, mailbox):
        """Unmark the mail as deleted"""
        old_mail = Mail(*mail.get())
        mail.local_flags = ''
        self.broker.UpdateMail(old_mail, mail, mailbox, session = True)
        self.notebook.DeletePanel(mail)
    
    def Delete(self, mail, mailbox):
        """Complete, permanent delete from the mailbox"""
        f = self.connections[mailbox]
        if isinstance(f, POP3Factory):
            d = f.proto.delete(int(mail.uid))
        elif isinstance(f, IMAP4Factory):
            m = imap4.MessageSet()
            m.add(int(mail.uid))
            d = f.proto.select(mail.folder)
            d.addCallback(lambda _: f.proto.addFlags(m, ['\\Deleted'], uid = True))
            d.addCallback(lambda _: f.proto.expunge())
            d.addCallback(lambda _: self.broker.DeleteMail(mailbox, mail, session = True))
            
    def FinalizeCheck(self, addframe, check_mailbox):
        d = self.broker.SaveMailbox(check_mailbox, session = True)
        d.addCallback(lambda _: self.broker.GetMailbox(check_mailbox, session = True))
        #d.addCallback(self.__FinalizeCheck, addframe = addframe, mailbox = check_mailbox)
        d.addCallback(self.__FinalizeCheck, addframe = addframe)
#        d.addCallback(lambda _: self.broker.GetData())
#        d.addCallback(self.PrintData)
    
    def PrintData(self, data):
        print pprint.pprint(data)
        
    def __FinalizeCheck(self, data, **kwargs):
        addframe = kwargs['addframe']
        mailbox = data
        addframe.UpdateCheckStatus(3, 'Finished SQL Save and Update')
        self.mailbox_mail[mailbox] = []
        print mailbox
        self.StartConnection(mailbox, sync = False)
        
    def MoveFolder(self, list_of_folders, new_parent, mailbox):
        """Move a list of folders into a new parent"""
        f = self.connections[mailbox]
        uid_folder = [(fold, self.GetUid(fold, mailbox)) for fold in list_of_folders]
        if isinstance(f, IMAP4Factory):
            d = f.proto.MoveFolder(uid_folder, new_parent)
            d.addBoth(self.HandleMoveFolder)
            
    def NewFolder(self, folder, mailbox):
        f = self.connections[mailbox]
        d = f.proto.NewFolder(folder)
        d.addBoth(self.HandleFolder)
            
    def HandleFolder(self, folder):
        print folder
    
    def GetUid(self, folder, mailbox):
        set = imap4.MessageSet()
        for uid in [m.uid for m in self.mailbox_mail[mailbox] if m.folder == folder]:
            set.add(uid)
        return set
    
    def HandleMoveFolder(self, obj):
        print "Here", obj
        
    def PurgeUID(self, mailbox, uids, folder):
        m = imap4.MessageSet()
        print folder, len(uids)
        if not uids:
            self.DisplayMailInGUI(mailbox, folder)
            return defer.succeed(m)
        mail_list = [d for d in self.mailbox_mail[mailbox] if d.folder == folder]
        for u in uids:
            found = False
            for mail in mail_list:
                if int(mail.uid) == int(u):
                    found = True
                    break
            if not found:
                m.add(int(u))
        return defer.succeed(m)
        
                
    def SaveMail(self, mail, mailbox, no_save = False):
        def Debug(d):
            print d
        if not isinstance(mail, list):
            mail = [mail]
        d = self.broker.SaveMailList(mail, mailbox, session = True)
        d.addCallback(self.HandleSave, mailbox = mailbox).addErrback(Debug)

    def NumberOfEmail(self, mailbox):
        """Returns the number of email in a mailbox"""
        return len(self.mailbox_mail[mailbox])
        
    def HandleSave(self, mail, **kwargs):
        box = kwargs['mailbox']
        print repr(box)
        print self.mailbox_mail.keys()
        self.mailbox_mail[box] += mail
        self.DisplayMail(mail, box)     
        
    def BeginSend(self, mail):
        """Initial creates a temporary folder containing the send folder"""#self.mailbox_tree.AddSend(mail, temporary = True)
        pass
        
    def EndSend(self, mail, mailbox, success):
        """Finalize the sent data"""
        #self.mailbox_tree.AddSend(mail, temporary = False,  success = success)
        if success:
            self.AddToSend(mail, mailbox)
            
    def AddToSend(self, mail, mailbox):
        f = self.connections[mailbox]
        if isinstance(f, IMAP4Factory):
            m = imap4.MessageSet()
            m.add(int(mail.uid))
            folder = self.GetSentFolder(mailbox)
            d = f.proto.select(mail.folder)
            d.addCallback(lambda _: f.proto.addFlags(m, ['\\Answered'], uid = True))
            if folder:
                d.addCallback(lambda _: f.proto.copy(m, folder, uid = True))
                self.SynchServer()
        
    def GetSentFolder(self, mailbox):
        folder = self.mailbox_folder[mailbox]
        for fl in folder:
            if 'sent' in fl.lower():
                return fl
        return None
    
    def Search(self, string, selection):
        """Search for the relevant string in all of the mailboxes"""
        if not self.connections:
            return 2
        for mailbox, f in self.connections.iteritems():
            if isinstance(f, POP3Factory):
                mail = self.mailbox_mail[mailbox]
                disp = []
                if selection['All'] or selection['Body']:
                    for m in mail:
                        if string in m.body:
                            disp.append(m)
                else:
                    for m in mail:
                        if selection['To'] and string in m.to:
                            disp.append(m)
                            continue
                        if selection['From'] and string in m.from_who:
                            disp.append(m)
                            continue
                        if selection['Subject'] and string in m.subject:
                            disp.append(m)
                            continue
                self.mailbox_tree.AddSearchResults(disp, mailbox)
            elif isinstance(f, IMAP4Factory):
                folder = self.mailbox_folder[mailbox]
                s = str(string)
                if selection['All']:
                    query = imap4.Query(text = s, all = True)
                else:
                    qlist = []
                    if selection['Body']:
                        qlist.append(imap4.Query(body = s))
                    if selection['From']:
                        q = {}
                        q['from'] = s
                        qlist.append(imap4.Query(**q))
                    if selection['To']:
                        qlist.append(imap4.Query(to = s))
                    if selection['Subject']:
                        qlist.append(imap4.Query(subject = s))
                    if qlist:
                        if len(qlist) - 1:
                            query = imap4.Or(*qlist)
                        else:
                            query = qlist[0]
                    else:
                        return False
                f.proto.Search(query = query, flist = folder[:], callback = self.HandleSearch, mailbox = mailbox)
        return True
    
    def AddEmailFromAddressBook(self, text, data):
        self.notebook.AddEmailFromAddressBook(text, data)

    def HandleSearch(self, uids, mailbox):
        """Handle the search results for uid"""
        uids = set(uids)
        m = []
        for uid in uids:
            m.append(self.GetMailFromUID(uid, mailbox))
        self.mail_list.AddSearchResults(m)
            
    def GetMailFromUID(self, uid, mailbox):
        mail = self.mailbox_mail[mailbox]
        for m in mail:
            if int(m.uid) == uid:
                return m
        
    def EndSearch(self):
        """End the search of data"""
        self.mail_list.EndSearchResults()
    
    def GetCurrentMailbox(self):
        """Returns the last active mailbox"""
        if hasattr(self, "current_mailbox"):
            return self.current_mailbox
        else:
            if len(self.mailbox_mail.keys()):
                return self.mailbox_mail.keys()[0]
            else:
                return None
            
    def SetCurrentMailbox(self, mailbox):
        """Set the last active mailbox""" 
        self.current_mailbox = mailbox
        
    def GetAddressBook(self):
        """Return the current address book"""
        return self.book_address
    
    def SentToDatabase(self, action, cls, *obj, **kw):
        """Save the relevant object to the database"""
        def InnerFunc(obj):
            print obj
        a = getattr(self.broker, action + cls)
        if kw.has_key("callback"):
            if not kw.has_key("get"):
                a(session = True, *obj).addCallback(kw['callback']).addErrback(InnerFunc)
            else:
                g = getattr(self.broker, "GetID" + cls)
                d = a(session = True, *obj)
                d.addCallback(InnerFunc)
                d.addCallback(lambda _: g(kw["get"]))
                d.addCallback(kw['callback']).addErrback(InnerFunc)
        else:
            a(*obj).addBoth(InnerFunc)
            
    def NewAttachment(self):
        """Create a new attachment in the current panel bar"""
        self.notebook.NewAttachment()
        
    def SaveAttachments(self):
        """Save the current attachments of the email"""
        self.notebook.SaveAttachments()
        
    def SetSynchGauge(self, mlist, clear = False, new = False):
        """Set the number of the mailboxes to synchronize"""
        if clear:
            self.folder_tree.ResetGaugeValue()
        self.folder_tree.ChangeSRange(len(mlist), new)
    
    def IncGauge(self):
        """Increment the gauge"""
        self.folder_tree.IncGaugeValue()
        
    def UpdateMailboxName(self, old_label, new_label):
        """Update the mailbox alias"""
        mailbox = self.GetMailboxFromName(old_label)
        print mailbox
        if mailbox:
            new_mailbox = Mailbox(*mailbox.get())
            new_mailbox.alias = new_label
            self.broker.UpdateMailbox(mailbox, new_mailbox)


    def GetMailboxFromName(self, old_label):
        for mailbox in self.mailbox_mail.keys():
            print old_label, mailbox.alias
            if mailbox.alias == old_label:
                return mailbox
            
    def GetNumberOfEmail(self, mailbox, folder):
        """Returns the number of emails in a mailboxes folder"""
        return len([d for d in self.mailbox_mail[mailbox] if d.folder == folder])
    
    def UpdateFolderName(self, mailbox, new_folder, old_folder, create = False):
        """Update the relevant folder name"""
        f = self.connections[mailbox]
        if not create:
            folder_list = [d for d in self.mailbox_mail[mailbox] if d.folder == old_folder]
            for mail in folder_list:
                new_mail = Mail(*mail.get())
                new_mail.folder = new_folder
                self.broker.UpdateMail(mail, new_mail, mailbox, session = True)
            if isinstance(f, IMAP4Factory):
                d = f.proto.rename(old_folder, new_folder)
        else:
            if isinstance(f, IMAP4Factory):
                d = f.proto.create(new_folder)
                
    def RemoveFolderList(self, folder_list, mailbox):
        """Delete the relevant folder list from the server"""
        fac = self.connections[mailbox]
        for f in folder_list:
            mail_list = [d for d in self.mailbox_mail[mailbox] if d.folder == f]
            self.broker.DeleteMailList(mail_list, mailbox, session = True)
            if isinstance(fac, IMAP4Factory):
                fac.proto.DeleteFolder(f)
            
    #class DatabaseHandler(object):
#    def __init__(self, vis):
#        """Initialize the database handler"""
#        self.vis = vis
#        self.mailbox_handler = MailboxHandler()
#        self.CreateDatabaseConnection()
#        self.Variables()
#    
#    def BindVisualization(self, tree):
#        """Bind the mailbox to the tree"""
#        self.mailbox_tree = tree
#        self.mailbox_handler.BindVisualization(tree)
#        
#    def BindMailNotebook(self, notebook):
#        """Bind the mail notebook to the mailbox handler"""
#        self.notebook = notebook
#        self.mailbox_handler.BindNotebook(notebook)
#        self.mailbox_handler.BindDatabase(self)
#        
#    def GetMailboxHandler(self):
#        """Returns the current mailbox handler"""
#        return self.mailbox_handler
#        
#    def CreateDatabaseConnection(self):
#        """Creates the database process and the relevant connection"""
#        self.manager = processing.Manager()
#        self.namespace = self.manager.Namespace()
#        self.DatabaseQuery = self.manager.Event()
#        self.TimeToQuit = self.manager.Event()
#        self.TimeToQuit.clear()
#        self.ArgQueue = self.manager.Queue()
#        self.ResultQueue = self.manager.Queue()
#        self.process = DatabaseProcess(self.namespace, self.DatabaseQuery,
#                                       self.TimeToQuit, self.ArgQueue,
#                                       self.ResultQueue)
#        self.process.start()
#
#        # Database setup, now ask for the owner
#        self.ArgQueue.put(['GetOwner', []])
#    
#    def OnIdle(self):
#        """When idle check the database connection"""
#        try:
#            result = self.ResultQueue.get(timeout = 0.0)
#            self.Decode(result)
#        except Empty:
#            pass
#    
#    def Variables(self):
#        """Class wide variables"""
#        self.control = None
#        self.owner = None
#        self.user = None
#        self.FirstTime = False
#        self.NoLogin = False
#        
#    def Package(self, *args):
#        """Pacakage the object using the IPC protocol of KuplaMail"""
#        ret = []
#        for c in args:
#            if isinstance(c, list):
#                ret.append('List')
#                ret.append(self.Package(*c))
#            else:
#                ret.append(c.oname())
#                ret.append(c.get())
#        return ret
#    
#    def Decode(self, result):
#        """Decode the query from the database, and tell the user"""
#        func = result[0]
#        attrs = result[1]
#        print func, attrs
#        if func.find('Get') != -1:
#            self.DecodeGet(func, attrs)
#        elif func.find('Validate') != -1:
#            self.DecodeValidate(func, attrs)
#        elif func.find('Save') != -1:
#            self.DecodeSave(func, attrs)
#            
#    def DecodeGet(self, func, attrs):
#        """Decode a get operation. Initial ownership check starts up the
#        application"""
#        if func.find('Owner') != -1:
#            if attrs:
#                self.owner = attrs[0]
#                self.user = User(*self.owner.get())
#                self.ArgQueue.put(['GetControl', []])
#            else:
#                self.InitialSetup()
#        elif func.find('Control') != -1:
#            self.control = attrs[0]
#            if attrs[0].password:
#                self.Login()
#            else:
#                self.NoLogin = True
#            self.ArgQueue.put(['GetUserMailbox', self.Package(self.user)])
#        elif func.find('UserMailbox') != -1:
#            for m in attrs:
#                if m.alias == self.control.lmailbox:
#                    self.mailbox_handler.SetCurrentMailbox(m)
#                self.ArgQueue.put(['GetMailboxMail', self.Package(m), '--with-children'])
#                self.ArgQueue.put(['GetMailboxDomain', self.Package(m)])
#                self.ArgQueue.put(['GetMailboxFolder', self.Package(m)])
#            self.mailbox_handler.AddMailboxes(attrs)
#        elif func.find('MailboxDomain') != -1:
#            self.mailbox_handler.AddDomain(attrs)
#        elif func.find('MailboxMail') != -1:
#            self.current_mailbox_mail = self.MailAttachments(attrs)
#            self.mailbox_handler.AddMail(self.current_mailbox_mail)
#        elif func.find('MailboxFolder') != -1:
#            self.mailbox_handler.AddEmptyFolders(attrs)
#            self.mailbox_handler.VisualizeMail()
#            if self.NoLogin:
#                self.ConnectToServer(self.mailbox_handler.GetDomains())
#                self.StartupTimers(self.mailbox_handler.GetDomains())
#                    
#    def DecodeSave(self, func, attrs):
#        """Decodes a database save operation"""
#        if func.find('Domain') != -1:
#            if self.FirstTime:
#                self.ConnectToServer(self.mailbox_handler.GetDomains())
#                self.StartupTimers(self.mailbox_handler.GetDomains())
#            elif self.save_state == 'RUNTIME':
#                self.addframe.UpdateCheckStatus(3, 'Finished SQL Save and Update')
#                self.mailbox_handler.AddMailboxes([self.check_mailbox])
#                self.mailbox_handler.AddDomain(self.check_domain)
#                self.SynchronizeIncomingServer(self.check_domain, self.check_mailbox)
#                self.check_mailbox.first_call = True
#                # Change to stateless
#                self.save_state = 'NONE'
#                
#    def DecodeValidate(self, func, attrs):
#        """Decode a validation request from the database"""
#        if func.find('User') != -1:
#            if attrs:
#                self.upframe.Close()
#                self.ConnectToServer(self.mailbox_handler.GetDomains())
#                self.StartupTimers(self.mailbox_handler.GetDomains())
#            else:
#                self.upframe.SetTitle('Login: Failed')
#    
#    def MailAttachments(self, mail):
#        """Attach relevant attachments to the email"""
#        ret = []
#        for m in mail:
#            if isinstance(m, list):
#                # Works because Mail only has one child
#                ret[-1].attachments = m
#            else:
#                ret.append(m)
#        return ret
#    
#    def ConnectToServer(self,domain):
#        """Iterate over all Control objects and connecting to the relvant mailboxes using
#        pop3 or imap4 where appropriate"""
#        for d in domain:
#            mailbox = self.mailbox_handler.GetDomainMailbox(d)
#            self.SynchronizeIncomingServer(d, mailbox)
#
#    def SynchronizeIncomingServer(self, d, mailbox):
#        """Synchronize with the incoming POP3 IMAP4 server"""
#        print d, mailbox
#        if d.protocol == 'pop3':
#            f = POP3Factory(str(d.username), str(d.password), self.HandlePOP3Results, d, mailbox)
#        elif d.protocol == 'imap4':
#            f = IMAP4Factory(str(d.username), str(d.password), self.CHMailboxList,
#                             self.HandleIMAP4Results, d, mailbox, h = self.mailbox_handler)
#        self.mailbox_handler.AddFactory(f, mailbox)
#        if d.issl:
#            reactor.connectSSL(str(d.iserver), d.iport, f, ssl.ClientContextFactory())
#        else:
#            reactor.connectTCP(str(d.iserver), d.iport, f)
#            
#    def StartupTimers(self, domain):
#        """Start up all the timers which query the database"""
#        print 'Here'
#        for d in domain:
#            # Since at this stage we are completely in the Twisted Event Loop, it
#            # is only natural to use callAfter for deterministic scheduled calls,
#            # such as periodic queries to a server.
#            reactor.callLater(d.dtime, self.OnTime, d)
#            
#    def OnTime(self, domain):
#        """Function handles regular queries to the male server"""
#        c = self.mailbox_handler.GetDomainFactory(domain)
#        # Peforms a check of the current mailbox size, and gets the new messages as needed.
#        c.proto.loggedIn(None)
#        reactor.callLater(domain.dtime, self.OnTime, domain)
#             
#    def CreateConnection(self, d, mailbox):
#        """Create a new connection to the server"""
#        mailbox.current_folder = None
#        if d.protocol == 'pop3':
#            f = POP3Factory(str(d.username), str(d.password), self.HandlePOP3Results, d, mailbox)
#        elif d.protocol == 'imap4':
#            f = IMAP4Factory(str(d.username), str(d.password), self.CHMailboxList,
#                             self.HandleIMAP4Results, d, mailbox, h = self.mailbox_handler)
#            self.IMAP4_start = True
#        self.mailbox_handler.AddFactory(f, mailbox)
#        if d.issl:
#            reactor.connectSSL(str(d.iserver), d.iport, f, ssl.ClientContextFactory())
#        else:
#            reactor.connectTCP(str(d.iserver), d.iport, f)
#            
#    def CHMailboxList(self, mlist, client, mailbox):
#        """Cycles through the folders of the IMAP4 database, querying for the
#        relevant data."""
#        def HandleError(results):
#            """Print out the error for debugging"""
#            return
#        if mlist:
#            m = mlist.pop(0)
#            mailbox.current_folder = m
#            client.GetMailbox(m, mlist).addErrback(HandleError)
#
#    def HandleIMAP4Results(self, mail, mail_list, client, domain, uids, flags):
#        """Handle the IMAP4 results, and save the relevant mail data"""
#        print 'Inside'
#        mailbox = self.mailbox_handler.GetDomainMailbox(domain)
#        self.ParseMailbox(mailbox, domain, mail, uids = uids, flags = flags)
#        if not mailbox.size:
#            mailbox.size = 1
#            # Save all of the mailbox folders
#            for folder in mail_list:
#                self.ArgQueue.put(['SaveFolder', self.Package(Folder(folder), mailbox, self.user)])
#            self.ArgQueue.put(['SaveMailbox', self.Package(mailbox, self.user)])
#        self.CHMailboxList(mail_list, client, mailbox)
#        if not mail_list:
#            self.IMAP4_directory = None
#            mailbox.current_folder = None
#            self.mailbox_handler.VisualizeMail(update = True)
#            
#
#    def ParseMailbox(self, mailbox, domain, message, uids = [], flags = []):
#        """Parse and control the mailbox"""
#        if self.control.lmailbox == '':
#            self.control.lmailbox = mailbox.alias
#            self.ArgQueue.put(['SaveControl', self.Package(self.control, self.owner)])
#            self.CurrentDomain = domain
#            print flags
#            print uids
#            if message and mailbox.current_folder:
#                self.SaveMessages(message[:], domain, mailbox.current_folder[:], uids = uids, flags = flags)
#        else:
#            if self.control.lmailbox == mailbox.alias:
#                self.CurrentDomain = domain
#
#            if message and mailbox.current_folder:
#                self.SaveMessages(message[:], domain, mailbox.current_folder[:], uids = uids, flags = flags) 
#        
#    def HandlePOP3Results(self, message, domain, uids):
#        """Function handles the POP3 results"""
#        mailbox = self.mailbox_handler.GetDomainMailbox(domain)
#        mailbox.current_folder = 'INBOX'
#        self.mailbox_handler.AddEmptyFolders(Folder(mailbox.current_folder))
#        self.ParseMailbox(mailbox, domain, message, uids = uids)
#        self.mailbox_handler.VisualizeMail(update = True)
#        
#    def SaveMessages(self, messages, d, directory, uids = [], flags = []):
#        """Save the messages to the database"""
#        mailbox = self.mailbox_handler.GetDomainMailbox(d) 
#        mm = []
#        matt = []
#        for key, message in enumerate(messages):
#            payload = message.get_payload()
#            if isinstance(payload, str):
#                if flags:
#                    fl, uid = self.ExtractUID(flags, key)
#                else:
#                    fl, uid = None, uids[key]
#                mm.append(self.SaveMail(payload, message, mailbox, message.get_content_type(), directory, uid, fl))
#            else:
#                mail = None
#                alternative = False
#                mixed = False
#                # Iterate over the entire message structure. Parse accordingly
#                for part in message.walk():
#                    if part.is_multipart():
#                        if part.get_content_subtype() == 'alternative':
#                            alternative = True
#                            mixed = False
#                        elif part.get_content_subtype() == 'mixed':
#                            mixed = True
#                            alternative = False
#                        elif part.get_content_subtype() == 'related':
#                            pass
#                        else:
#                            print 'Error:', part.get_content_type()
#                        continue
#                    if part.get_content_maintype() == 'text':
#                        load = part.get_payload()
#                        filename = part.get_filename()
#                        #FCFS: to deal with erroneous and/or complicated MIME architectures
#                        if not mail:
#                            if flags:
#                                fl, uid = self.ExtractUID(flags, key)
#                            else:
#                                fl, uid = None, uids[key]
#                            mail = self.SaveMail(load, message, mailbox, part.get_content_type(), directory, uid, fl)
#                            mm.append(mail)
#                        else:
#                            att = Attachment(filename, load, len(load), part.get_content_type(), alternative)
#                            #matt.append([att, mail])
#                            self.ArgQueue.put(['SaveAttachment', self.Package(att, mail)])
#                            mail.attachments.append(att)
#                        continue
#                    elif part.get_content_maintype() == 'application':
#                        load = part.get_payload()
#                        filename = part.get_filename()
#                        att = Attachment(filename, load, len(load), part.get_content_type())
#                        self.ArgQueue.put(['SaveAttachment', self.Package(att, mail)])
#
#                        #matt.append([att, mail])
#                        mail.attachments.append(att)
#                        continue
#                    name = part.get_filename(None)
#                    if not name:
#                        ext = mimetypes.guess_extension(part.get_content_type())
#                        name = 'anon%s' % ext 
#                    print 'Error:', part.get_content_type()
#    
#        mailbox.size = mailbox.size + len(mm)
#        if directory == '':
#            directory = 'Inbox'
#
#        # Update Mailbox Tree
#        folder = Folder(directory)
#        if not mailbox.first_call:
#            self.mailbox_handler.AddMailboxes([mailbox])
#            mailbox.first_call = True
#        self.mailbox_handler.AddMail(mm, it = False, folder = folder.alias)
#        #self.ArgQueue.put(['SaveBulkMail', self.Package(mm, mailbox, self.user)])
#        #self.ArgQueue.put(['SaveBulkAttachment',self.Package(matt)])
#        #self.ArgQueue.put(['SaveFolder', self.Package(folder, mailbox, self.user)])
#        self.ArgQueue.put(['SaveMailbox', self.Package(mailbox, self.user)])
#        
#    def ExtractUID(self, flags, key):
#        """Extract the flags and uid from the flags dictionary"""
#        key = select(flags.keys(), key)
#        keys = flags[key]
#        fl = ''
#        uid = int(keys['UID'])
#        for k in keys['FLAGS']:
#            fl = fl + k + " "
#        return fl, uid
#        
#    def SaveMail(self, payload, message, mailbox, t, directory, uid, fl):
#        """Save the mail to the resident database extracting important state information"""
#        dt = datetime.datetime.fromtimestamp(time.mktime(parsedate(message['Date'])))
#        if directory:
#            received = 'Received:%s' % directory
#        else:
#            received = 'Received'
#        mail = Mail(dt, message['From'], payload, received, iencoding = t,  isubject = message['Subject'],
#                    icc = message['cc'] or '', ibcc = message['bcc'] or '',
#                    ito = message['To'], imessage_id = message['Message-ID'], iuid = uid, iflags = fl)
#        self.ArgQueue.put(['SaveMail', self.Package(mail, mailbox, self.user)])
#        return mail
#    
#    def Login(self):
#        """Open a UserPassFrame and login in the user"""
#        self.upframe = UserPassFrame(self.vis, self)
#        self.upframe.Show()
#
#    def ValidateUser(self, username, password):
#        """Callback from the UserPassFrame to ask if the user is valid"""
#        self.user = User(username, password)
#        self.ArgQueue.put(['ValidateUser', self.Package(self.user)])
#
#    def InitialSetup(self):
#        """Open the wizard and get relevant informatiom about the owner"""
#        wizard = SetupWizard(self.vis, 'Kupla Mail Setup')
#        values = wizard.run()
#        if values:
#            owner_page = values[0]
#            self.owner = Owner(owner_page[0], owner_page[4])
#            self.user = User(owner_page[0], owner_page[4])
#            self.control = Control(owner_page[1], owner_page[2], owner_page[3])
#
#            # Save
#            self.ArgQueue.put(['SaveOwner', self.Package(self.owner)])
#            self.ArgQueue.put(['SaveUser', self.Package(self.user, self.owner)])
#            self.ArgQueue.put(['SaveControl', self.Package(self.control, self.owner)])
#        else:
#            self.InitialSetup()
#    
#    def FinalizeCheck(self, addframe, d, m):
#        """Save the current data from the add frame"""
#        self.check_domain = d
#        self.check_mailbox = m
#        self.addframe = addframe
#        self.save_state = 'RUNTIME'
#        self.ArgQueue.put(['SaveMailbox', self.Package(m, self.user)])
#        self.ArgQueue.put(['SaveDomain', self.Package(d, m, self.user)])
#    
#    def OnQuit(self):
#        """Close the database connection"""
#        def IgnoreError(error):
#            """Ignore any error since we are quitting"""
#            pass
#        self.TimeToQuit.set()
#        self.process.join()
#        for f in self.mailbox_handler.GetFactories():
#            try:
#                if isinstance(f, POP3Factory):
#                    f.proto.quit().addErrback(IgnoreError)
#                elif isinstance(f, IMAP4Factory):
#                    f.proto.logout().addErrback(IgnoreError)
#            except Exception, e:
#                print e
#    
#    def GetCurrentDomain(self):
#        """Return the current domain"""
#        return self.mailbox_handler.GetCurrentDomain()
#    
#    def NewAttachment(self):
#        """Create a new attachment in the current panel bar"""
#        self.notebook.NewAttachment()
#        
#    def SaveAttachments(self):
#        """Save the current attachments of the email"""
#        self.notebook.SaveAttachments()
#        
#    def DeleteMail(self, mailbox, mail):
#        """Delete the mail from the mailbox"""
#        self.ArgQueue.put(['DeleteMail', self.Package(mail, mailbox)])
#        self.ArgQueue.put(['SaveMailbox', self.Package(mailbox, self.user)])
#    